﻿using ExcelDna.Integration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelPlugin {
    public static class XmlLoader {
        public class LibReferRecord{
            public string excelPath { get; private set; }
            List<XmlValidEntry> m_validRecord = new List<XmlValidEntry>();
	        public List<XmlValidEntry> ValidRecord
            {
                get { return m_validRecord; }
            }

            List<XmlClassSet> m_referClass = new List<XmlClassSet>();
            List<XmlMetalib> m_referlib = new List<XmlMetalib>();
            public void AddReferClass(XmlClassSet classSet) 
            {
                m_referClass.Add(classSet);
                if(!m_referlib.Contains(classSet.ParentLib)) {
                    m_referlib.Add(classSet.ParentLib);
                }
            }
            public List<XmlClassSet> GetReferClass() {
                return m_referClass;
            }
            public List<XmlMetalib> GetReferLib() {
                return m_referlib;
            }
            public LibReferRecord(string _p) {
                excelPath = _p;
            }
        }

        private static Dictionary<string, LibReferRecord> m_excelRecordMap = new Dictionary<string, LibReferRecord>();  //存下每个打开过的wordbook和Xml引用的map
        private static Dictionary<string, XmlMetalib> m_allLib = new Dictionary<string, XmlMetalib>();
        private static List<XmlMetalib> m_commonLib = new List<XmlMetalib>();
        private static bool m_xmlLoadFlag = false;

        #region xml载入
        private static void _parseXmlFile(string fullPath, bool common_use = false) {
            try {
                XmlSerializer serializer = new XmlSerializer(typeof(XmlMetalib));
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                ns.Add(string.Empty, string.Empty);
                FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read);
                XmlMetalib mt = serializer.Deserialize(fs) as XmlMetalib;
                mt.fullPath = fullPath;
                fs.Close();

                mt.CheckAndAssemble(common_use);
                if(common_use) {
                    m_commonLib.Add(mt);
                }
                else {
                    if(m_allLib.ContainsKey(mt.lib_name)) {
                        MsgHelper.PopErrorMsg("MetaLib: {0}名称定义重复", mt.lib_name.ToString());
                        return;
                    }
                    m_allLib.Add(mt.lib_name, mt);
                }
            }
            catch(Exception e) {
                MsgHelper.PopErrorMsg("载入XML文件异常: {0}", e.ToString());
                return;
            }
            return;
        }
        public static void LoadXmlConfigs() {
            if(m_xmlLoadFlag) return;
            try {
                var app = ExcelDnaUtil.Application as Microsoft.Office.Interop.Excel.Application;
                int index = app.ActiveWorkbook.FullName.LastIndexOf(WS_PATH.WS_PATH_SOURCE);
                if(index < 0) {
                    return;
                }
                string config_path = Path.Combine(app.ActiveWorkbook.FullName.Substring(0, index), WS_PATH.WS_PATH_CONFIG);
                DirectoryInfo dir = new DirectoryInfo(config_path);
                //先载入共有定义
                FileInfo[] commonFI = dir.GetFiles(FILE_PATH.XML_COMMON_DEFINE);
                if(commonFI.Length > 0) {
                    string full_path = Path.Combine(config_path, commonFI[0].Name);
                    _parseXmlFile(full_path, true);
                }
                FileInfo[] fileInfo = dir.GetFiles("*.xml");
                foreach(FileInfo item in fileInfo) {
                    if(string.Compare(FILE_PATH.XML_COMMON_DEFINE, item.Name) == 0) {
                        continue;
                    }
                    string full_path = Path.Combine(config_path, item.Name);
                    _parseXmlFile(full_path);
                }
                m_xmlLoadFlag = true;
            }
            catch(System.Exception e) {
                MsgHelper.PopErrorMsg("获取XML配置目录失败: {0}", e.ToString());
            }
        }
        //重新载入Xml配置
        public static void ReloadXmlConfigs() {
            m_excelRecordMap.Clear();
            m_allLib.Clear();
            m_commonLib.Clear();
            GC.Collect();
            m_xmlLoadFlag = false;
            var app = ExcelDnaUtil.Application as Excel.Application;
            OperationUtil.OnWorkbookActivate(app.ActiveWorkbook);
        }
        #endregion
        public static void SetupCurrenExcelRecord() {
            if(m_excelRecordMap.ContainsKey(GlobalBlackBoard.cur_wb_relative_path)) {
                return;  //已经存在记录则不建立,防止反复切换导致反复处理 
            }
            LibReferRecord record = new LibReferRecord(GlobalBlackBoard.cur_wb_relative_path);
            m_excelRecordMap.Add(GlobalBlackBoard.cur_wb_relative_path, record);

            //建立当前Excel的索引
            foreach(var lib_pair in m_allLib) {
                XmlMetalib lib = lib_pair.Value;
                foreach(var cit in lib.class_config) {
                    foreach(var ds in cit.DataSourceList) {
                        if(ds.tablename == GlobalBlackBoard.cur_wb_relative_path) {
                            record.AddReferClass(cit);
                            if(lib.valid_config == null) {
                                continue;
                            }
                            foreach(var item in lib.valid_config.validEntryList) {
                                if(item.class_name == cit.name) {
                                    record.ValidRecord.Add(item);
                                }
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 获取定义处的单元格 
        /// </summary>
        /// <param name="headName">引用处的表头名</param>
        /// <param name="cellValue">引用处的引用值</param>
        /// <returns></returns>
        public static Excel.Range GetDefineCell(string headName, string cellValue) {
            Excel.Range result = null;
            //根据当前Excel名获取ValidSet配置
            LibReferRecord record;
            if(!m_excelRecordMap.TryGetValue(GlobalBlackBoard.cur_wb_relative_path, out record)) return null;

            XmlValidEntry target_entry = null;
            //查找column_name是否有headName值的配置 
            foreach(XmlValidEntry entry in record.ValidRecord) {
                if(headName == entry.column_name) {
                    target_entry = entry;
                    break;
                }
            }
            if(target_entry == null) return null;

            //找到ref_lib ref_class,获取对应的ClassSet中的datasource
            XmlMetalib ref_lib;
            if(!m_allLib.TryGetValue(target_entry.ref_metalibName, out ref_lib)) return null;
            XmlClassSet ref_class = ref_lib.GetClassSet(target_entry.ref_className);
            if(ref_class == null) return null;

            //遍历datasource找到定义处单元格，返回此单元格对象
            foreach(XmlDataSource ds in ref_class.DataSourceList) {
                var app = ExcelDnaUtil.Application as Excel.Application;
                var curWBPath = app.ActiveWorkbook.FullName;
                string filePath = curWBPath.Substring(0, curWBPath.LastIndexOf(WS_PATH.WS_PATH_SOURCE) + WS_PATH.WS_PATH_SOURCE.Length);

                string fullname = Path.Combine(filePath, ds.tablename);
                Excel.Workbook wb;
                bool formerOpen = true;
                if(!GlobalBlackBoard.CurOpenWB.TryGetValue(fullname, out wb)) {
                    formerOpen = false;
                    wb = app.Workbooks.Open(fullname);
                }
                if(wb == null) continue;
                Excel.Worksheet sheet = wb.Worksheets[1];
                if(sheet == null) continue;
                Excel.Range headCell = sheet.Rows[1].Find(target_entry.ref_columnName);
                if(headCell == null) continue;

                Excel.Range defineCell = sheet.Columns[headCell.Column].Find(cellValue);
                if(defineCell != null) {
                    result = defineCell;
                    break;
                }

                //原先没打开的workbook直接关闭
                if(!formerOpen) {
                    wb.Close();
                }
            }

            return result;
        }
        static XmlBaseSet getReferSetByName(XmlMetalib lib, string name) {
            //现在自己lib中找，找不到去common找

            XmlBaseSet res;
            if(!lib.ReferenceSets.TryGetValue(name, out res)) {
                bool findFlag = false;
                string findNameRecord = string.Empty;
                foreach(XmlMetalib lib_it in m_commonLib) {
                    if(lib_it.ReferenceSets.TryGetValue(name, out res)) {
                        if(findFlag) {
                            MsgHelper.PopErrorMsg("枚举：【{0}】重复定义", name);
                            break;
                        }

                        findFlag = true;
                    }
                }
            };

            return res;
        }
        static XmlFieldEntry recursiveGetCfg(List<XmlFieldEntry> fieldList, string raw_headName, string[] name_parts, int cur_index) {
            if(cur_index < name_parts.Length) {
                foreach(XmlFieldEntry entry in fieldList) {
                    if(entry.key == raw_headName) {
                        return entry;
                    }
                    else if(!string.IsNullOrEmpty(entry.type_param) && entry.key == name_parts[cur_index]) {
                        XmlBaseSet refer = getReferSetByName(entry.ParentSet.ParentLib, entry.type_param);
                        if(refer != null) {
                            XmlBaseRefer referobj = refer as XmlBaseRefer;
                            if(referobj != null) {
                                return recursiveGetCfg(referobj.fieldCfg, raw_headName, name_parts, ++cur_index);
                            }
                            else {
                                return entry;
                            }
                        }
                    }
                }
            }
            return null;

        }
        static XmlFieldEntry getXmlFieldByMakeName(LibReferRecord record, string name, string[] name_splitted) {
            foreach(XmlClassSet cs in record.GetReferClass()) {
                XmlFieldEntry cfg_entry = recursiveGetCfg(cs.fieldCfg, name, name_splitted, 0);
                if(cfg_entry != null) {
                    return cfg_entry;
                }
            }
            return null;
        }
        public static List<string> GetEnumList(string raw_headName, string[] headNameParts, string name, Excel.Worksheet sheet, bool main_sheet) {
            List<string> res = new List<string>();
            LibReferRecord record;
            if(m_excelRecordMap.TryGetValue(name, out record)) {

                XmlFieldEntry cfg_entry = null;
                if(main_sheet) {
                    cfg_entry = getXmlFieldByMakeName(record, raw_headName, headNameParts);      
                }
                else {
                    //不是主表的话，要根据子表名找出当前子表属于主表哪一列，再通过xml定义找到是属于哪一个group或subclass,传入fieldList
                    string[] name_part = sheet.Name.Split('.');
                    cfg_entry = getXmlFieldByMakeName(record, sheet.Name, name_part);      

                    if(cfg_entry != null && cfg_entry.type == XML_DATA_TYPE_NAME.GROUP) {
                        XmlBaseSet groupBase = getReferSetByName(cfg_entry.ParentSet.ParentLib, cfg_entry.type_param);
                        if(groupBase != null) {
                            XmlGroupSet groupSet = groupBase as XmlGroupSet;
                            cfg_entry = recursiveGetCfg(groupSet.fieldCfg, raw_headName, headNameParts, 0);
                        }
                    }
                }

                if(cfg_entry != null && cfg_entry.type == XML_DATA_TYPE_NAME.ENUM) {
                    XmlBaseSet enumBase = getReferSetByName(cfg_entry.ParentSet.ParentLib, cfg_entry.type_param);
                    if(enumBase != null) {
                        XmlEnumSet enumSet = enumBase as XmlEnumSet;
                        foreach(var item in enumSet.EntryList) {
                            res.Add(item.key);
                        }
                    }
                }
            }

            return res;
        }
        public static string GetDefineFileNames() {
            LibReferRecord rec;
            if(!m_excelRecordMap.TryGetValue(GlobalBlackBoard.cur_wb_relative_path, out rec)) {
                return string.Empty;
            }

            StringBuilder sb = new StringBuilder();
            foreach(XmlMetalib lib in rec.GetReferLib()) {
                sb.AppendLine(lib.fullPath);
            }
            return sb.ToString();
        }
    }
}
